<?php
/**
 * Created by PhpStorm.
 * User: WJP
 * Date: 2019-04-13
 * Time: 10:08
 */

// +----------------------------------------------------------------------
// 2017-8-23 09:03:40
// 此次修改为只作为websocket的服务端
// +----------------------------------------------------------------------
class Server{
    protected $swoole;
    // 监听所有地址
    protected $host = '0.0.0.0';
    // 监听 9501 端口
    protected $port = 9501;
    // 配置项
    protected $option = [
        //设置启动的worker进程数
        'worker_num' => 2,
        //task进程的数量
        'task_worker_num' => 4,
        //指定task任务使用消息队列模式，3表示完全争抢模式，task进程会争抢队列，无法使用定向投递
        'task_ipc_mode' => 3,
        //task进程的最大任务数
        'task_max_request' => 1000,
        // 守护进程化
        'daemonize'  => false,
        // 监听队列的长度
        'backlog'    => 128,
        //绑定uid时用
        'dispatch_mode' => 5,
        //设置日志路径
        'log_file' => SWOOLE_LOG_PATH,
    ];
    protected function init(){
        //异步非阻塞多进程的websocket
        $this->swoole = new swoole_websocket_server($this->host, $this->port);
        $eventList    = ['Open', 'Message', 'Close', 'HandShake' , 'Task', 'Finish' , 'WorkerStart' , 'Receive'];

        // 设置参数
        if (!empty($this->option)) {
            $this->swoole->set($this->option);
        }
        // 设置回调
        foreach ($eventList as $event) {
            if (method_exists($this, 'on' . $event)) {
                $this->swoole->on($event, [$this, 'on' . $event]);
            }
        }
    }
    public function start(){
        $this->init();
        $this->swoole->start();
    }
    public function getHost(){
        return $this->host;
    }
    public function getPort(){
        return $this->port;
    }
    /**
     * [onOpen 建立连接时的回调函数]
     * @method onOpen
     * @param  swoole_server       $serv [description]
     * @param  swoole_http_request $req    [description]
     * @return [type]                      [description]
     */
    public function onOpen(swoole_server $serv, swoole_http_request $req){
        //将连接绑定到uid上面
        if(!empty($req->get)&&$req->get['uid']){
            $serv->bind($req->fd , $req->get['uid']);
        }
    }
    /**
     * [onMessage 接收到socket客户端发送数据的回调函数]
     * @method onMessage
     * @param  swoole_server          $serv [description]
     * @param  swoole_websocket_frame $frame  [description]
     * @return [type]                         [description]
     */
    public function onMessage(swoole_server $serv,  swoole_websocket_frame $frame){
        //收到数据时处理数据
        //根据收到的cmd名字去调用指定的方法
        $receive = json_decode($frame->data,true);

        //为了避免数据处理量过大阻塞当前进程，导致服务响应变慢，我们把耗时的操作扔到TaskWorker进程池中执行
        //当要执行的方法存在并且已经在swoole_log表里备案过的可以丢到task进程池
//        $swooleLog = D('SwooleLog');
        $swooleController = A('Swoole');
        //$receive['args']['id']是业务服务器那边数据库SwooleLog表里的id
//        if (method_exists($swooleController, $receive['cmd']) && $receive['args']['id']) {
        if ($receive['args']['id']) {
            $task_id = $serv->task($receive);
            //记录task_id信息
            //...其他你想要做的精细化处理
        }else{
            if($receive['cmd'] === 'reload'){
                //利用Swoole提供的柔性终止/重启的机制
                $rs = $serv -> reload();
                $serv->push($frame->fd , $rs);
            }elseif($receive['args']['id']){
                $returnData = $this->_returnStr('submit_error');
                $serv->push($frame->fd , $returnData);
            }elseif(method_exists($this, $receive['cmd'])){
                $this->{$receive['cmd']}($serv , $frame);
            }
        }
    }
    /**
     * [onTask 在task_worker进程池内被调用时的回调函数]
     * @method onTask
     * @param  swoole_server $serv          [description]
     * @param  int           $task_id       [description]
     * @param  int           $src_worker_id [description]
     * @param  mixed         $data          [description]
     * @return [type]                       [description]
     */
    public function onTask(swoole_server $serv, $task_id, $src_worker_id, $data){
        //记录任务开始执行的时间
        //...自己发挥  我们项目的业务逻辑部分已经删除
        //
        try{
            $swooleController = A('Start');
            var_dump($swooleController);
            $rs = $swooleController->{$data['cmd']}($data['args']);
            var_dump($rs);
            return json_encode(['id'=>$data['args']['id'] , 'status' => true , 'other' => $rs]);
        }catch(\Exception $e){
            return json_encode(['id'=>$data['args']['id'] , 'status' => false , 'other' => $e->getMessage()]);
        }
    }
    /**
     * [onFinish 任务执行完毕时的回调函数]
     * @method onFinish
     * @param  swoole_server $serv    [description]
     * @param  int           $task_id [description]
     * @param  string        $data    [description]
     * @return [type]                 [description]
     */
    public function onFinish(swoole_server $serv, $task_id, string $data){
        $rs = json_decode($data , true);
//        $swooleLog = D('SwooleLog');
        //...
        //通知用户该任务已经执行完毕了 可以来查看数据了
        //检查客户端链接是否存在 如果存在的话发送消息
        $returnData = $this->_returnStr('task_excute_success');
        foreach($serv->connections as $fd){
            $conn = $serv->connection_info($fd);
            //根据uid判断当前连接是活跃的
            //这里是业务服务器那边能取到的用户信息，这里已经删除，保护我们的项目
            $serv->push($fd , $returnData);
        }
    }
    /**
     * [onWorkerStart 为了应用能热加载 把框架的东西放到worker进程启动]
     * @method onWorkerStart
     * @param  swoole_server $server    [description]
     * @param  [type]        $worker_id [description]
     * @return [type]                   [description]
     */
    public function onWorkerStart(swoole_server $server, $worker_id){
        // 定义应用目录
        define('APP_PATH', '../Application/');
        // 定义应用模式
        define('APP_Mode', 'cli');
        define('BIND_MODULE','Cli');
        // 引入ThinkPHP入口文件
        require '../ThinkPHP/ThinkPHP.php';
    }
    /**
     * [_returnStr 返回给客户端的数据]
     * @method _returnStr
     * @param  [type]     $type [description]
     * @return [type]           [description]
     */
    private function _returnStr($type){
        switch ($type) {
            case 'submit_success':
                $returnData = [
                    'code' => 202,
                    'info' => '任务已经提交，请等待服务器计算数据！'
                ];
                break;
            case 'submit_error':
                $returnData = [
                    'code' => 404,
                    'info' => '任务提交失败，请联系管理员进行处理！'
                ];
                break;
            case 'task_excute_success':
                $returnData = [
                    'code' => 200,
                    'info' => '任务执行完毕！'
                ];
                break;
        }
        return json_encode($returnData);
    }
    /**
     * [clients 获取所有的客户端连接信息，返回我们的uid]
     * @method clients
     * @return [type] [description]
     */
    public function clients(swoole_server $serv , swoole_websocket_frame $frame){
        //...
        $serv->push($frame->fd , json_encode($serv->connections));
    }
    public function __call($method, $args){
        call_user_func_array([$this->swoole, $method], $args);
    }
}